import BaseObject from "../base/BaseObject";
import {pool} from "../base/Pool";
import Event from "./Event";

export default class EventDispatcher extends BaseObject {

    constructor() {
        super();
        this.enabled = true;
        this._dipatchLevel = 0;
        this._listenerBins = {};
    }

    static toString() {
        return '[class EventDispatcher]';
    }

    /**
     *
     * @param type
     * @returns {boolean}
     */
    hasEventListener(type) {
        return type in this._listenerBins[type];
    }

    /**
     *
     * @param type
     * @param listener
     * @param thisTarget
     * @param data
     * @param priority
     */
    addEventListener(type, listener, thisTarget, data= null, priority = 0) {
        this.removeEventListener(type, listener);
        let listenerBins = this._listenerBins[type];
        if (listenerBins) {
            if (this._dipatchLevel !== 0) {
                this._listenerBins[type] = listenerBins = listenerBins.concat();
            }
        } else {
            this._listenerBins[type] = listenerBins = [];
        }
        listenerBins.push({ listener: listener, thisTarget: thisTarget, data: data, priority: priority });
    }

    /**
     *
     * @param type
     * @param listener
     * @param data
     */
    removeEventListener(type, listener, data = null) {
        let listenerBins = this._listenerBins[type];
        if (listenerBins) {
            if (this._dipatchLevel !== 0) {
                this._listenerBins[type] = listenerBins = listenerBins.concat();
            }

            for (let i = 0, l = listenerBins.length; i < l; ++i) {
                const listenerBin = listenerBins[i];
                if (listenerBin.listener === listener && this._isSameListener(listenerBin.data, data)) {
                    listenerBins.splice(i, 1);
                    break;
                }
            }
        }
    }

    /**
     *
     * @param type
     */
    removeAllEventListener(type) {
        if (type) {
            delete this._listenerBins[type];
        } else {
            Object.keys(this._listenerBins).map(key => {
                delete this._listenerBins[key];
            });
        }
    }

    /**
     *
     * @param event
     */
    dispatchEvent(event) {
        const type = event.type;
        const listenerBins = this._listenerBins[type];
        if (listenerBins) {
            event.target = this;
            if (!event.currentTarget) {
                event.currentTarget = event.target;
            }

            this._dipatchLevel++;
            for (let listenerBin of listenerBins) {
                listenerBin.listener.call(listenerBin.thisTarget, event, listenerBin.data);
            }
            this._dipatchLevel--;
        }
    }

    dispatchEventWith(type, data = null) {
        if (this.hasEventListener(type)) {
            let event = pool.getItemByClass('Event', Event);
            event.data = data;
            this.dispatchEvent(event);
            event.dispose();
            pool.recover('Event', event);
        }
    }

    _onClear() {
        Object.keys(this._listenerBins).map(key => {
            delete this._listenerBins[key];
        });
        this.enabled = true;
        this._dipatchLevel = 0;
    }

    _isSameListener(a, b) {
        return a === b;
    }
}